package org.annoflow.javassist;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

/**
 * A factory for creating Command objects. This factory takes a command provided
 * in a javassist configuration file and produces a command.
 */
public class CommandFactory
{
	private static final String CLOUDSPACE_VM_JAVASSIST = "org.annoflow.javassist.";
	private static Logger logger = Logger.getLogger(CommandFactory.class);
	/** The macro hash. */
	public static HashMap<String, MacroDefinition> macroHash = new HashMap<String, MacroDefinition>();

	/**
	 * Creates a command object from a list of arguments parsed from the command
	 * line.
	 * 
	 * @param args
	 *            the arguments parsed out of a javassist configuration file
	 * 
	 * @return the created command
	 * 
	 * @throws MalformedCommandException
	 *             the malformed command exception
	 * @throws MalformedMacroException
	 *             the malformed macro exception
	 */
	public static CommandInterface createCommand(List<String> args)
			throws MalformedCommandException, MalformedMacroException
	{
		cleanArgs(args);
		String commandType = args.get(0);
		String command = args.get(args.size() - 1);
		Pattern macroPattern = Pattern.compile("%[a-zA-z0-9]+\\([^\\)]*\\)");
		Matcher macros = macroPattern.matcher(command);
		// Find Macros in the replacement text
		while (macros.find())
		{
			String inlineMacro = macros.group();
			macros = macroPattern.matcher(command.substring(macros.end()));
			MacroCall inline = new MacroCall(inlineMacro);
			MacroDefinition definition = macroHash.get(inline.getMacroName());
			if (definition == null)
			{
				throw new MalformedMacroException("The macro call, "
						+ inline.getMacroName()
						+ ", does not match any known definitions");
			}
			String replacement = definition.getTailoredCommand(inline);
			String fixedCommand = command.replace(inlineMacro, replacement);
			args.set(args.size() - 1, fixedCommand);
		}
		try
		{
			Class<?> clazz = Class.forName(CLOUDSPACE_VM_JAVASSIST
					+ commandType);
			Constructor<?> constr = clazz
					.getDeclaredConstructor(new Class<?>[] { java.util.List.class });
			CommandInterface reflectCommand = (CommandInterface) constr
					.newInstance(new Object[] { args });
			return reflectCommand;
		}
		catch (Exception e)
		{
			logger.error("The command " + commandType
					+ " does not have a matching command class.",e);
		}
		throw new MalformedCommandException("The translationType " + command
				+ " is unknown in command:  " + args.toString());
	}

	/**
	 * this cleans an argument list to remove arguments that are known to add no
	 * meaning to a command.
	 * 
	 * @param args
	 *            the args
	 */
	private static void cleanArgs(List<String> args)
	{
		args.remove("with");

	}

	/**
	 * Registers a macro to the command factory. The macros are automatically
	 * used when creating commands to create clean commands without macro
	 * references in them.
	 * 
	 * @param curMacro
	 *            the cur macro
	 */
	public static void registerMacro(MacroDefinition curMacro)
	{
		macroHash.put(curMacro.getMacroName(), curMacro);

	}
}
